package com.zmn.brs.business.impl.engineer;

import com.alibaba.fastjson.JSONObject;
import com.zmn.brs.business.impl.base.BaseBServiceImpl;
import com.zmn.brs.business.interfaces.dingtalk.DingTalkBService;
import com.zmn.brs.business.interfaces.engineer.EngineerSalesBService;
import com.zmn.brs.business.interfaces.rule.AlarmRuleBService;
import com.zmn.brs.common.constant.BrsConst;
import com.zmn.brs.common.constant.RedisKeyConst;
import com.zmn.brs.common.enums.EventTypeEnum;
import com.zmn.brs.common.enums.ExpressionEnum;
import com.zmn.brs.common.enums.SceneTypeEnum;
import com.zmn.brs.dubbo.constant.DubboConsts;
import com.zmn.brs.model.bo.rule.CustomRuleBO;
import com.zmn.brs.model.entity.event.Event;
import com.zmn.brs.model.entity.record.EventRecord;
import com.zmn.brs.model.query.rule.EngineerRuleCustomQuery;
import com.zmn.brs.model.vo.rule.SalesRuleVO;
import com.zmn.brs.model.vo.rule.UnpaidRuleVO;
import com.zmn.brs.model.vo.rule.common.EngineerRuleVO;
import com.zmn.brs.services.interfaces.event.EventService;
import com.zmn.brs.services.interfaces.record.EventRecordService;
import com.zmn.brs.utils.BrsUtil;
import com.zmn.common.dto2.ReportDTO;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.consts.GlobalConsts;
import com.zmn.cube.dubbo.dto.SnapshotReportDIO;
import com.zmn.cube.dubbo.interfaces.board.BoardRemoteService;
import com.zmn.engineer.common.dto.engineer.BaseEngineerDRO;
import com.zmn.engineer.dubbo.interfaces.engineer.EngineerListRemoteService;
import com.zmn.mcc.common.dto.staff.StaffDRO;
import com.zmn.mcc.dubbo.interfaces.staff.StaffListRemoteService;
import com.zmn.plat.common.dictionary.StatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 描述： 工程师收款未支付金额监控实现
 *
 * @author chongw
 * @since 2021-07-08 9:55
 */
@Slf4j
@Service
public class EngineerSalesBServiceImpl extends BaseBServiceImpl<EngineerRuleVO> implements EngineerSalesBService {

    final static String TAG = "工程师监控派单客单价实现" ;
    @Resource
    private AlarmRuleBService alarmRuleBService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    BoardRemoteService boardRemoteService;

    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    EngineerListRemoteService engineerListRemoteService;

    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    StaffListRemoteService staffListRemoteService;

    @Resource
    private EventRecordService eventRecordService;

    @Resource
    private DingTalkBService dingTalkBService;

    @Resource
    private EventService eventService;

    /**
     * 计算派单客单价
     *
     * @param eventType
     * @param smsRuleId
     * @param pushTopic
     * @return
     */
    @Override
    public Boolean compute(Integer eventType, Integer staffId, Integer smsRuleId, String pushTopic) {
        try {
            EngineerRuleCustomQuery query = EngineerRuleCustomQuery.builder()
                    .eventType(EventTypeEnum.ENGINEER_SALES.getTypeId())
                    .build();
            //查询工程师配置数据
            List<CustomRuleBO> items = Optional.ofNullable(alarmRuleBService.listPageRuleByEventQuery(query)).orElse(new ArrayList<>());
            if (items.isEmpty()) {
                return false;
            }
            //获取工程师公共阈值
            EngineerRuleVO vo = this.getCommonThreshold(eventType);
            if (StringUtil.isBlank(vo.getDaySnapshotId())) {
                log.error("[{}], 当前事件未配置当天派单客单价快照ID ！", TAG);
                return false;
            }
            if (StringUtil.isBlank(vo.getMonthSnapshotId())) {
                log.error("[{}], 当前事件未配置当月派单客单价快照ID ！", TAG);
                return false;
            }
            Integer daySnapshotId = Integer.parseInt(vo.getDaySnapshotId());
            Integer monthSnapshotId = Integer.parseInt(vo.getMonthSnapshotId());
            //当天派单客单价
            SnapshotReportDIO todaySnapshotReportDIO = this.initCubeParams(daySnapshotId, staffId);
            //当月派单客单价
            SnapshotReportDIO monthSnapshotReportDIO = this.initCubeParams(monthSnapshotId, staffId);
            //获取cube 当天数据
            ReportDTO<Map<String, Object>> todayDTO = boardRemoteService.getSnapshotReport(todaySnapshotReportDIO);
            log.info("[{}], 获取当日派单客单价，快照ID：{}, cube响应：{} ，初始化开始时间：{}", TAG, daySnapshotId, JSONObject.toJSON(todayDTO), DateUtil.getNowFormatted());
            //获取cube 当月数据
            ReportDTO<Map<String, Object>> monthDTO = boardRemoteService.getSnapshotReport(monthSnapshotReportDIO);
            log.info("[{}], 获取当月派单客单价，快照ID：{}, cube响应：{} ，初始化开始时间：{}", TAG, monthSnapshotId, JSONObject.toJSON(monthDTO), DateUtil.getNowFormatted());
            //遍历工程师收款未支付金额规则
            items.forEach(o -> {
                if (StatusEnum.DISABLE.getCode().equals(o.getRealStatus())) {
                    log.error("[{}], 规则ID：{} ,ThresholdId：{},规则已被停用！", TAG, o.getRuleId(), o.getThresholdId());
                    return;
                }
                this.compute(eventType, o, todayDTO, monthDTO);
            });
            //调用消息推送
            this.pushJob(eventType, smsRuleId, pushTopic);

            return true;
        } catch (Exception e) {
            log.error("[{}], 参数：{}，工程师派单客单价监控异常，异常信息：", TAG, e);
        }
        return false;
    }

    /**
     * 计算工程师派单客单价
     *
     * @param eventType
     * @param customRuleBO
     * @param todayDTO
     * @param monthDTO
     */
    protected void compute(Integer eventType, CustomRuleBO customRuleBO,
                           ReportDTO<Map<String, Object>> todayDTO, ReportDTO<Map<String, Object>> monthDTO) {
        if (!monthDTO.isSuccess()) {
            return;
        }
        Map<String, Object> info = Optional.ofNullable(monthDTO.getInfo()).orElse(new HashMap<>());
        Set<Object> measures = (HashSet<Object>) info.get("measures");
        Object measureId = measures.toArray()[0];
        Map<String, Double> todayData = new HashMap<>();

        //当日派单客单价
        List<Map<String, Object>> todayItems = Optional.ofNullable(todayDTO.getItemRows()).orElse(new ArrayList<>());
        todayItems.forEach(ir -> {
            Object xxxObj = ir.get(BrsUtil.DIMENSION_X_X_X);
            Object valueObj = ir.get(BrsUtil.buildKey(String.valueOf(measureId)));
            if (valueObj != null && xxxObj != null) {
                Double value = BrsUtil.getCubeValueFormat(valueObj.toString());
                todayData.put(xxxObj.toString(), value);
            }
        });
        //获取规则信息
        SalesRuleVO salesRuleVO = JSONObject.parseObject(customRuleBO.getThresholdValue(), SalesRuleVO.class);
        //当月派单客单价
        List<Map<String, Object>> monthItems = Optional.ofNullable(monthDTO.getItemRows()).orElse(new ArrayList<>());

        monthItems.forEach(ir -> {
            Object xxxObj = ir.get(BrsUtil.DIMENSION_X_X_X);
            Object xxtObj = ir.get(BrsUtil.DIMENSION_X_X_T);
            Object valueObj = ir.get(BrsUtil.buildKey(String.valueOf(measureId)));
            if (valueObj != null && xxxObj != null) {
                String[] xxx = BrsUtil.splitStr(xxxObj.toString());
                String[] xxt = BrsUtil.splitStr(xxtObj.toString());
                Long companyId = Long.parseLong(xxx[0]);
                if (salesRuleVO.getOrgThrId().longValue() != companyId.longValue()) {
                    return;
                }
                //获取工程师当日派单客单价
                BigDecimal todayValue = BigDecimal.valueOf(Optional.ofNullable(todayData.get(xxxObj.toString())).orElse(0.0D));
                //当天派单客单价为0，则不监控
                if (todayValue.doubleValue() <= 0) {
                    return;
                }
                //获取工程师当月派单客单价
                BigDecimal monthValue = BigDecimal.valueOf(BrsUtil.getCubeValueFormat(valueObj.toString()));
                if (monthValue.doubleValue() <= 0) {
                    return;
                }
                Integer engineerId = Integer.parseInt(xxx[2]);
                String redisHashKey = RedisKeyConst.getTodayRedisKey(eventType, String.format("company:%s", companyId));
                BigDecimal realValue = todayValue.divide(monthValue, 4, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100));
                Boolean isWarn = false;
                //判断当前数据是否小于阈值
                if (ExpressionEnum.LT.getExpression().equals(salesRuleVO.getExpression()) && realValue.doubleValue() < salesRuleVO.getValue()) {
                    isWarn = true;
                }
                //判断当前数据是否大于阈值
                else if (ExpressionEnum.GT.getExpression().equals(salesRuleVO.getExpression()) && realValue.doubleValue() > salesRuleVO.getValue()) {
                    isWarn = true;
                }
                //是否触发预警
                if (isWarn) {
                    //工程师主管
                    String engSupervisorName = "" ;
                    if (xxt.length > 1) {
                        engSupervisorName = xxt[1];
                    }
                    log.info("[{}], companyId：{}, engineerId：{} ,已触发工程师派单客单价监控预警！实际值：{}，阈值：{}", TAG, companyId, engineerId, realValue, salesRuleVO.getValue());
                    //查询工程师信息
                    BaseEngineerDRO engineer = this.getEngineerByEngineerId(engineerId);
                    JSONObject expandJson = new JSONObject();
                    expandJson.put("todayValue", todayValue);
                    expandJson.put("monthValue", monthValue);
                    expandJson.put("realValue", realValue);
                    expandJson.put("expression", salesRuleVO.getExpression());
                    expandJson.put("thresholdValue", salesRuleVO.getValue());
                    expandJson.put("engineerId", engineerId);
                    expandJson.put("engineerName", engineer.getRealname());
                    expandJson.put("companyId", companyId);
                    expandJson.put("engSupervisorName", engSupervisorName);
                    //缓存推送数据redis key
                    String todayRedisKey = RedisKeyConst.getTodayRedisKey(eventType,
                            DateUtil.getNowFormatted(DateUtil.FORMAT_DATE2),
                            companyId,
                            ExpressionEnum.getExpressionEnum(salesRuleVO.getExpression()).name().toLowerCase());
                    log.info("[{}], todayRedisKey：{}", TAG, todayRedisKey);
                    redisTemplate.opsForHash().put(todayRedisKey, String.valueOf(engineerId), expandJson.toJSONString());
                    redisTemplate.expire(todayRedisKey, 3, TimeUnit.DAYS);
                    EventRecord record = EventRecord.builder()
                            .objectId(engineerId.longValue())
                            .thresholdId(customRuleBO.getThresholdId())
                            .sceneType(SceneTypeEnum.ENGINEER_TARGET.getTypeId())
                            .eventCode(customRuleBO.getEventCode())
                            .messageSend(GlobalConsts.NO)
                            .realName(customRuleBO.getThresholdName())
                            .expand(expandJson.toJSONString())
                            .createTime(DateUtil.getNow())
                            .monitorVersion(redisHashKey)
                            .build();
                    //保存事件监控数据
                    Integer flag = eventRecordService.insert(record);
                    if (flag > 0) {
                        log.info("[{}], companyId：{} ，engineerId：{} ，保存事件记录成功！！！", TAG, companyId, engineerId);
                    } else {
                        log.error("[{}], companyId：{}，engineerId：{} ，保存事件记录失败！！！", TAG, companyId, engineerId);
                    }
                }
            }
        });
    }

    /**
     * 工程师派单客单价消息推送
     *
     * @param eventType
     * @param smsRuleId
     * @param pushTopic
     */
    public void pushJob(Integer eventType, Integer smsRuleId, String pushTopic) {
        EngineerRuleCustomQuery query = EngineerRuleCustomQuery.builder()
                .eventType(eventType)
                .build();
        //查询派单客单价规则
        List<CustomRuleBO> salesRuleList = alarmRuleBService.listRuleByEventQuery(query);
        salesRuleList.forEach(o -> {
            if (StatusEnum.DISABLE.getCode().equals(o.getRealStatus())) {
                log.error("[{}], 规则ID：{} ,ThresholdId：{},规则已被停用！！！", TAG, o.getRuleId(), o.getThresholdId());
                return;
            }
            SalesRuleVO ruleVO = JSONObject.parseObject(o.getThresholdValue(), SalesRuleVO.class);
            Integer companyId = ruleVO.getOrgThrId();
            String companyName = ruleVO.getOrgThrName();
            Double thresholdValue = ruleVO.getValue();
            ruleVO.setThresholdId(o.getThresholdId());
            //缓存推送数据redis key
            String todayRedisKey = RedisKeyConst.getTodayRedisKey(eventType,
                    DateUtil.getNowFormatted(DateUtil.FORMAT_DATE2),
                    companyId,
                    ExpressionEnum.getExpressionEnum(ruleVO.getExpression()).name().toLowerCase());
            //获取预警数据
            Map<Object, Object> warnData = Optional.ofNullable(redisTemplate.opsForHash().entries(todayRedisKey)).orElse(new HashMap<>());
            log.info("[{}]预警缓存数据={}", TAG, JSONObject.toJSONString(warnData));
            if (warnData.isEmpty()) {
                log.info("[{}], 规则ID：{} ,ThresholdId：{}，子公司：{}，当前子公司无派单客单价监控数据！！！", TAG, o.getRuleId(), o.getThresholdId(), companyId);
                return;
            }
            StringBuffer sb = new StringBuffer();
            //工程师主管名称
            List<String> listName = new ArrayList<>();
            warnData.forEach((k, v) -> {
                JSONObject data = JSONObject.parseObject(v.toString());
                String engSupervisorName = (String) data.get("engSupervisorName");
                engSupervisorName = (engSupervisorName == null ? "" : engSupervisorName);
                if (!listName.contains(engSupervisorName)) {
                    listName.add(engSupervisorName);
                }
            });
            for (String item : listName) {
                sb.append(String.format("工程师所属主管：%s\n", (item.equals("") ? "暂无" : item)));
                warnData.forEach((k, v) -> {
                    JSONObject data = JSONObject.parseObject(v.toString());
                    String engSupervisorName = (String) data.get("engSupervisorName");
                    engSupervisorName = (engSupervisorName == null ? "" : engSupervisorName);
                    if (item.equals(engSupervisorName)) {
                        Object engineerName = data.get("engineerName");
                        Object engineerId = data.get("engineerId");
                        Object todayValue = data.get("todayValue");
                        Object monthValue = data.get("monthValue");
                        sb.append(String.format("「%s」当天派单客单价%s-当月派单客单价%s\n", engineerName, todayValue, monthValue));
                    }
                });
            }
            if (sb != null && StringUtil.isNotBlank(sb.toString())) {
                StringBuffer msg = new StringBuffer();
                msg.append("【工程师派单客单价预警】\n");
                msg.append(String.format("%s以下工程师派单客单价 %s %s %s，请时刻关注！\n",
                        companyName,
                        ruleVO.getExpression(),
                        BrsUtil.formatDoubleToStr(thresholdValue),
                        "%"));
                msg.append(sb);
                msg.append(String.format("%s数据", DateUtil.getNowFormatted().replace(" ", " 截止 ")));
                log.info("thresholdId={},ruleVO={}", o.getThresholdId(), ruleVO);
                //获取事件ID
                Integer robotId = ruleVO.getRobotId();
                if (NumberUtil.isNullOrZero(robotId)) {
                    log.info("robotId={}为空，取默认群", robotId);
                    Event event = Optional.ofNullable(eventService.findByEventCode(o.getEventCode())).orElse(Event.builder().build());
                    robotId = event.getRobotId();
                }
                dingTalkBService.sendDingtalkGroup(robotId, msg.toString());
                String receiverIdStr = ruleVO.getReceiverId();
                log.info("配置发送钉钉人员={}", receiverIdStr);
                if (!(StringUtil.isBlank(receiverIdStr))) {
                    StringBuilder dtalkIdBuilder = new StringBuilder();
                    String[] receiverIds = null;
                    if (receiverIdStr.contains(BrsConst.COMMA)) {
                        //多个用户
                        receiverIds = receiverIdStr.split(BrsConst.COMMA);
                    } else {
                        //单个用户
                        receiverIds = new String[]{receiverIdStr};
                    }
                    for (int i = 0; i < receiverIds.length; i++) {
                        Integer receiverId = Integer.valueOf(receiverIds[i]);
                        //获取接收人信息
                        ResponseDTO<StaffDRO> staffDTO = staffListRemoteService.getStaff(receiverId);
                        if (!staffDTO.isSuccess()) {
                            log.error("[{}], 获取接收人信息失败，staffId：{}", TAG, receiverId);
                            continue;
                        }
                        StaffDRO receiverStaff = Optional.ofNullable(staffDTO.getData()).orElse(new StaffDRO());
                        String dtalkId = receiverStaff.getDTalkId();
                        if (dtalkIdBuilder.length() > 0) {
                            dtalkIdBuilder.append(",");
                        }
                        dtalkIdBuilder.append(dtalkId);
                        if (StringUtil.isBlank(dtalkId)) {
                            log.error("[{}], 获取接收人钉钉ID为空，staffId：{}", TAG, receiverId);
                        }
                    }
                    if (dtalkIdBuilder.length() > 0) {
                        JSONObject data = new JSONObject();
                        /*data.put("eventName" , EventTypeEnum.ENGINEER_SALES.getTypeName());
                        data.put("companyName" , companyName);
                        data.put("thresholdValue" , String.format("%s %s", ruleVO.getExpression(), thresholdValue));*/
                        data.put("contentMsg", msg.toString());
                        dingTalkBService.sendDingTalk(dtalkIdBuilder.toString(), data.toJSONString(), smsRuleId, pushTopic);
                    }
                }
            }
        });
    }

    /**
     * 根据工程师ID查询工程师信息
     *
     * @param engineerId
     * @return
     */
    protected BaseEngineerDRO getEngineerByEngineerId(Integer engineerId) {
        try {
            //获取工程师信息
            ResponseDTO<BaseEngineerDRO> responseDTO = engineerListRemoteService.getEngineerByEngineerId(engineerId);
            BaseEngineerDRO engineerDRO = Optional.ofNullable(responseDTO.getData()).orElse(new BaseEngineerDRO());
            return engineerDRO;
        } catch (Exception e) {
            log.error("[{}], engineerId：{} ，获取工程师接口异常！！！，异常信息：{}", TAG, engineerId, e);
        }
        return new BaseEngineerDRO();
    }


    /**
     * 描述：初始化cube入参
     *
     * @param snapshotId
     * @return
     * @author chongw
     * @since 2021-06-07 17:50
     */
    protected SnapshotReportDIO initCubeParams(Integer snapshotId, Integer staffId) {
        //定义快照入参
        SnapshotReportDIO snapshot = new SnapshotReportDIO();
        //快照ID
        snapshot.setSnapshotId(snapshotId);
        snapshot.setDebug(true);
        snapshot.setStaffId(staffId);
        snapshot.setDayLimit(BrsConst.CUBE_SNAPSHOT_DAY_LIMIT);
        log.info("[{}], 快照ID：{}, cube入参：{} ，初始化开始时间：{}", TAG, snapshotId, JSONObject.toJSON(snapshot), DateUtil.getNowFormatted());
        return snapshot;
    }

}
